﻿// ================================================================================
// The contents of this file are subject to the Mozilla Public License
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/2.0/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
// ================================================================================
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using Reflex.Os.Aion.Service;

namespace Reflex.Os.Aion.View
{
	/// <summary>
	/// Represents the ChatView.
	/// </summary>
	partial class ChatView : Page
	{
		/// <summary>
		/// Initialize a new instance of the ChatView class.
		/// </summary>
		public ChatView()
		{
			// Initialize the component.
			InitializeComponent();

			// Occurs when the data context for this element changes.
			DataContextChanged += ( sender, e ) =>
			{
				// Check if the data context is of the ChatConversationService type.
				if ( DataContext is ChatConversationService )
				{
					// Add an event handler to the message collection.
					( DataContext as ChatConversationService ).Message.CollectionChanged += ( collectionSender, collectionEvent ) =>
					{
						// Check if a message is added.
						if ( collectionEvent.Action == NotifyCollectionChangedAction.Add )
						{
							// Scroll to the bottom.
							ScrollViewer.ScrollToBottom();

							// Flash the window.
							_FlashWindow( uint.MaxValue );
						}
					};
				}
			};
			
			// Occurs when the element has been loaded.
			Loaded += ( sender, e ) =>
			{
				// Retrieve the window.
				Window hWindow = Window.GetWindow( this );

				// Occurs before a window is closed.
				hWindow.Closing += ( closingSender, closingEvent ) =>
				{
					// Minimize the window.
					( closingSender as Window ).WindowState = WindowState.Minimized;

					// Hide the window.
					( closingSender as Window ).Hide();

					// Cancel the closing.
					closingEvent.Cancel = true;
				};

				// Occurs when the window becomes a background window.
				hWindow.Deactivated += ( deactivatedSender, deactivatedEvent ) =>
				{
					// Check if the window is not minimized.
					if ( hWindow.WindowState != WindowState.Minimized )
					{
						// Iterate through the ItemsControl.
						for ( int i = 0; i < ItemsControl.Items.Count; i++ )
						{
							// Retrieve the content presenter.
							ContentPresenter hContentPresenter = ItemsControl.ItemContainerGenerator.ContainerFromIndex( i ) as ContentPresenter;

							// Check if the content presenter is available.
							if ( hContentPresenter != null )
							{
								// Retrieve the data template.
								DataTemplate hDataTemplate = hContentPresenter.ContentTemplate;

								// Set the foreground color of the textblock.
								(( TextBlock ) hDataTemplate.FindName( "TextBlock", hContentPresenter )).Foreground = Brushes.Gray;
							}
						}
					}
				};

				// Flash the window.
				_FlashWindow( uint.MaxValue );
			};

			// Occurs when a key is pressed while focus is on this element.
			Queue.KeyDown += ( sender, e ) =>
			{
				// Check if this is the enter key.
				if ( e.Key == Key.Enter )
				{
					// Initialize a new instance of the ButtonAutomationPeer class.
					ButtonAutomationPeer hButtonAutomationPeer = new ButtonAutomationPeer( Send );

					// Retrieve the invoke provider for the button.
					IInvokeProvider hInvokeProvider = hButtonAutomationPeer.GetPattern( PatternInterface.Invoke ) as IInvokeProvider;

					// Invoke the button.
					hInvokeProvider.Invoke();
				}
			};
		}

		#region PInvoke
		/// <summary>
		/// Flashes the specified window. It does not change the active state of the window.
		/// </summary>
		/// <param name="pwfi">A pointer to a FLASHWINFO structure.</param>
		/// <returns></returns>
		[DllImport( "user32.dll" )]
		private static extern bool FlashWindowEx( ref FLASHWINFO pwfi );

		/// <summary>
		/// Contains the flash status for a window and the number of times the system should flash the window.
		/// </summary>
		[StructLayout( LayoutKind.Sequential )]
		private struct FLASHWINFO
		{
			/// <summary>
			/// The size of the structure in bytes.
			/// </summary>
			public uint cbSize;
			/// <summary>
			/// A Handle to the Window to be Flashed. The window can be either opened or minimized.
			/// </summary>
			public IntPtr hwnd;
			/// <summary>
			/// The Flash Status.
			/// </summary>
			public uint dwFlags;
			/// <summary>
			/// The number of times to Flash the window.
			/// </summary>
			public uint uCount;
			/// <summary>
			/// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink rate.
			/// </summary>
			public uint dwTimeout;
		}

		/// <summary>
		/// Flash the window.
		/// </summary>
		/// <param name="iCount">Number of times to flash.</param>
		private void _FlashWindow( uint iCount )
		{
			// Find the window.
			Window hWindow = Window.GetWindow( this );

			// Check if the window is valid.
			if ( hWindow != null )
			{
				FLASHWINFO hFLASHWINFO = new FLASHWINFO();
				hFLASHWINFO.cbSize = Convert.ToUInt32( Marshal.SizeOf( hFLASHWINFO ));
				hFLASHWINFO.hwnd = new WindowInteropHelper( hWindow ).Handle;
				hFLASHWINFO.dwFlags = 0x2 | 0xC;
				hFLASHWINFO.uCount = iCount;
				hFLASHWINFO.dwTimeout = 0;
				FlashWindowEx( ref hFLASHWINFO );
			}
		}
		#endregion
	}
}